/*
 *  renderer.cpp
 *  
 *  Created by James Dykstra
 *	Team 5
 *
 *  Copyright 2010 Michigan Technological University. All rights reserved.
 */

#include "renderer.h"
#include "sprite.h"

/*Renderer()
/ Initializes SDL video and audio subsystems, and sets up the screen.
*/
Renderer::Renderer()
{
	//Outputs any errors to a log, output.txt
	std::fstream log("log.txt", std::fstream::out | std::fstream::trunc);
	log<<"Initializing SDL:        ";

	//Initializing SDL subsystems
	if(SDL_Init(SDL_INIT_VIDEO)==-1)
		log<<"ERROR: Failed to initialize SDL!\n";

	log<<"Done"<<std::endl<<"Creating OpenGL Context: ";

	//Establishing the screen at the specified resolution
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	screen = SDL_SetVideoMode(RES_WIDTH, RES_HEIGHT, SCREEN_BPP, SDL_OPENGL);
	if(screen == NULL)
		log<<"ERROR: Failed to initialize and create screen!\n";

	log<<"Done"<<std::endl<<"Setting Perspective:     ";

	//Enable textures and set clear color to black
	glEnable(GL_TEXTURE_2D);
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

	//Establish viewport and clear color buffer
	glViewport(0, 0, RES_WIDTH, RES_HEIGHT);
	glClear(GL_COLOR_BUFFER_BIT);

	//Setting the view
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	glOrtho(0.0, RES_WIDTH, RES_HEIGHT, 0.0, -1.0, 1.0);

	//Set to modelview for drawing/texturing
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	log<<"Done\n";

	log.close();
}
/*~Renderer()
/ Cleans up all our valid pointers and shuts down the SDL subsystems.
*/
Renderer::~Renderer()
{
	if(screen!=NULL)
		SDL_FreeSurface(screen);

	SDL_Quit();
}
/*drawSurface()
/ Draws a given texture at the specified position.
/ pos - rect containing coordinates and size
/ angle - the angle in degrees that this image is facing
/ texture - the texture id of the image to be drawn
*/
void Renderer::drawSurface(SDL_Rect *pos, GLfloat angle, GLuint texture)
{
	glLoadIdentity();

	if(angle != 0.f)
	{
		//Calculate w/2 and h/2 in order to translate into the center of the object and rotate it about its center
		GLfloat wT = (GLfloat)pos->w/2;
		GLfloat hT = (GLfloat)pos->h/2;
		glTranslatef((GLfloat)pos->x + wT, (GLfloat)pos->y + hT, 0.f);
		glRotatef(angle, 0.f, 0.f, 1.0f);
		glTranslatef(-wT, -hT, 0.f);
	}
	else
		glTranslatef((GLfloat)pos->x, (GLfloat)pos->y, 0.f);

	glBindTexture(GL_TEXTURE_2D, texture);

	//Enabling alpha
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	glBegin(GL_QUADS);
		glTexCoord2i(0, 0);
		glVertex3f(0.f, 0.f, 0.f);

		glTexCoord2i(1, 0);
		glVertex3f((GLfloat)pos->w, 0.f, 0.f);

		glTexCoord2i(1, 1);
		glVertex3f((GLfloat)pos->w, (GLfloat)pos->h, 0.f);

		glTexCoord2i(0, 1);
		glVertex3f(0.f, (GLfloat)pos->h, 0.f);
	glEnd();
}
/*loadImage()
/ Loads an image file as an OpenGL texture and returns the texture id or null on failure.
/ filename - The file path to the image file to be loaded.
*/
SDL_Surface* Renderer::loadImage(std::string filename)
{
	SDL_Surface *img    = NULL;
	SDL_Surface *optimg = NULL;

	img = IMG_Load(filename.c_str());
	//Optimizing the image into the proper display format
	if(img != NULL)
	{
		optimg = SDL_DisplayFormatAlpha(img);

		//Send the SDL_Surface off to be used to create an OpenGL texture and return the id
		if(optimg != NULL)
		{
			SDL_FreeSurface(img);
			return optimg;
		}
		else
			return img;
	}
	return NULL;
}
/*update()
/ Flips the buffer, to update screen.
/ For use by the Game Engine ONLY.
/ Must be called once per iteration of the game loop.
*/
void Renderer::update()
{
	SDL_GL_SwapBuffers();
	glClear(GL_COLOR_BUFFER_BIT);
}
/*getPixelAt()
/ Returns a pixel from the given SDL_Surface at the specified coordinates.
/ Used for per-pixel collision detection.
*/
Uint32 Renderer::getPixelAt(SDL_Surface *surface, int x, int y)
{
	//Calculating the pixel's representation
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * (surface->format->BytesPerPixel);

	//Returns the pixel in the proper format
    switch(surface->format->BytesPerPixel)
	{
    case 1:
        return *p;
		break;

    case 2:
        return *(Uint16 *)p;
		break;

    case 3:
        if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
            return p[0] << 16 | p[1] << 8 | p[2];
        else
            return p[0] | p[1] << 8 | p[2] << 16;
		break;

    case 4:
        return *(Uint32 *)p;
		break;

    default:
        return 0;
		break;
    }
}
//Method used to consolidate the code to create an OpenGL texture from an SDL_Surface.
GLuint Renderer::createTexture(SDL_Surface *surf)
{
	if(surf == NULL)
		return NULL;

	if(isPow2(surf->w) && isPow2(surf->h))
	{
		SDL_Surface *texSurf = SDL_CreateRGBSurface(SDL_SWSURFACE, surf->w, surf->h, 32, surf->format->Rmask, surf->format->Gmask, surf->format->Bmask, surf->format->Amask);

		SDL_SetAlpha(surf, 0, 0);
		SDL_SetAlpha(texSurf, 0, 0);
		SDL_BlitSurface(surf, 0, texSurf, 0);

		GLuint texBuf = NULL;
		GLenum textureFmt;
		GLint numColors;

		numColors = texSurf->format->BytesPerPixel;
		if(numColors == 4)
			if(texSurf->format->Rmask == 0x000000ff)
				textureFmt = GL_RGBA;
			else
				textureFmt = GL_BGRA;
		else if(numColors == 3)
			if(texSurf->format->Rmask == 0x000000ff)
				textureFmt = GL_RGB;
			else
				textureFmt = GL_BGR;
		else
			return texBuf;

		glGenTextures(1, &texBuf);
		glBindTexture(GL_TEXTURE_2D, texBuf);

		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

		glTexImage2D(GL_TEXTURE_2D, 0, numColors, texSurf->w, texSurf->h, 0, textureFmt, GL_UNSIGNED_BYTE, texSurf->pixels);

		SDL_FreeSurface(texSurf);

		return texBuf;
	}
	else
	{
		std::fstream log("log.txt", std::fstream::out | std::fstream::app);
		log<<"SDL_Surface: "<<surf<<" does not have power of 2 dimensions!"<<std::endl;
		log<<"w: "<<surf->w<<" | h: "<<surf->h<<std::endl;
		log.close();

		return NULL;
	}
}

int Renderer::getScreenWidth(){
	return screen->w;
}
int Renderer::getScreenHeight(){
	return screen->h;
}

void Renderer::drawBox(int cx, int cy, int w, int h, float angle)
{
	glLoadIdentity();

	if(angle != 0.f)
	{
		//Calculate w/2 and h/2 in order to translate into the center of the object and rotate it about its center
		glTranslatef((GLfloat)cx, (GLfloat)cy, 0.f);
		glRotatef(angle, 0.f, 0.f, 1.0f);
		glTranslatef((GLfloat)-(w/2), (GLfloat)-(h/2), 0.f);
	}
	else
		glTranslatef((GLfloat)(cx-(w/2)), (GLfloat)(cy-(h/2)), 0.f);

	glBegin(GL_QUADS);
		glColor3f(255, 0, 0);
		glVertex3f(0.f, 0.f, 0.f);

		glVertex3f((GLfloat)w, 0.f, 0.f);

		glVertex3f((GLfloat)w, (GLfloat)h, 0.f);

		glVertex3f(0.f, (GLfloat)h, 0.f);
		glColor3f(255, 255, 255);
	glEnd();
}
